<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Matching Test</title>
    <style>
        body{
            font-family: sans-serif;
        }
        table td{
            padding: 1em 2em;
        }
    </style>
</head>
<body>
<h2>Relevance Scoring Comparison</h2>
<h4>Indexed Text: "Gulliver's Travels" (Swift Jonathan 1726)</h4>
<hr>
<div id="container">
    <table>
        <tr>
            <th><b>Query</b></th>
            <th>flexsearch</th>
            <th>bulksearch</th>
            <th>elasticlunr</th>
            <th>lunr</th>
            <th>wade</th>
            <th>fuse</th>
            <th>jssearch</th>
            <th>jsii</th>
            <th>bm25</th>
            <th>fuzzysearch</th>
        </tr>
        <tr id="test-1">
            <td style="width: 200px"></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-2">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-3">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-4">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-5">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-6">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-7">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-8">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-9">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-10">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-11">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr id="test-12">
            <td></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
        <tr>
            <td colspan="10">
                <br>
                Contextual Search Test:
            </td>
        </tr>
        <tr id="test-13">
            <td style="width: 200px"></td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
            <td>wait ...</td>
        </tr>
    </table>
</div>
<hr>
<div style="line-height: 2em">
    <div style="display:inline-block; width:16px; height:16px; background: #f00"></div> Either no results or relevant content was not included in results.<br>
    <div style="display:inline-block; width:16px; height:16px; background: orange"></div> Most relevant results was not found in the first place.<br>
    <div style="display:inline-block; width:16px; height:16px; background: #0a0"></div> Most relevant results was successfully found in the first place.<br>
    <b>Note:</b> Open console and type e.g. <i>data[493]</i>
</div>
<script src="../dist/flexsearch.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/nextapps-de/bulksearch@master/bulksearch.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/weixsong/elasticlunr.js@master/example/elasticlunr.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lunr@2.1.6/lunr.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/kbrsh/wade@master/dist/wade.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/krisk/Fuse@3.0.4/dist/fuse.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-search@1.3.7/dist/umd/js-search.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/karussell/jsii@master/web/js/src/BitSet.js"></script>
<script src="https://cdn.jsdelivr.net/gh/karussell/jsii@master/web/js/src/JSii.js"></script>
<script src="https://gistcdn.githack.com/vlad-x/a25e0c5c1eeb6bf6aa38/raw/02d1a1703e4a99a7c733c85097f583579f6af4e2/bm25.js"></script>
<script src="https://rawcdn.githack.com/jeancroy/FuzzySearch/cbcdd8307d70a209b1cbf17a535158d5c21840d7/dist/FuzzySearch.min.js"></script>
<script src="../data/gulliver.js"></script>
<script>

    var data = [];

    setTimeout(function(){

        var new_data = text_data;
        var tmp = '';

        for(var i = 0; i < new_data.length; i++){

            if(new_data[i].length > 2) {

                tmp += new_data[i] + '. ';

                if((tmp.length > 1000) || (i === new_data.length - 1)){

                    data.push(tmp.replace(/{[^}]*}/g, ''));
                    tmp = '';
                }
            }
        }

        data.push('zero one two three four five six seven eight nine ten.');
        data.push('zero one two three four five six seven eight nine ten.');
        data.push('four two zero one three ten five seven eight six nine.');
        data.push('zero one two three four five six seven eight nine ten.');
        data.push('zero one two three four five six seven eight nine ten.');

        tmp = null;
        new_data = null;
        text_data = null;

        var bulksearch;
        var flexsearch;
        var elasticsearch;
        var lunrsearch;
        var wade;
        var fuse;
        var jssearch;
        var jsii;
        var bm25;
        var fuzzysearch;

        // -----------------------------------------------------------

        bulksearch = new BulkSearch({
            type: 'short', // this type specifies the maximum bitlength of assigned IDs!
            encode: 'extra',
            multi: true
        });

        console.time('bulksearch');

        for(var i = 0; i < data.length; i++){
            bulksearch.add(i, data[i]);
        }

        console.timeEnd('bulksearch');

        // -----------------------------------------------------------

        flexsearch = new FlexSearch({
            encode: 'extra',
            tokenize: 'strict',
            threshold: 0,
            resolution: 9,
            depth: 3
        });

        console.time('flexsearch');

        for(var i = 0; i < data.length; i++){
            flexsearch.add(i, data[i]);
        }

        console.timeEnd('flexsearch');

        // -----------------------------------------------------------

        elasticsearch = elasticlunr(function () {
            this.setRef('id');
            this.addField('content');
        });

        console.time('elasticsearch');

        for(var i = 0; i < data.length; i++){
            elasticsearch.addDoc({id: i, content: data[i]});
        }

        console.timeEnd('elasticsearch');

        // -----------------------------------------------------------

        console.time('lunr');

        lunrsearch = lunr(function(){
            this.ref('id');
            this.field('content');
            for(var i = 0; i < data.length; i++){
                this.add({id: i, content: data[i]});
            }
        });

        console.timeEnd('lunr');

        // -----------------------------------------------------------

        var wadesearch = function(query){
            return wade(query).sort(sort_by_score_down);
        };

        function sort_by_score_down(a, b){
            var sum = a.score - b.score;
            return sum < 0 ? 1 : sum > 0 ? -1 : 0;
        }

        console.time('wade');
        wade = Wade(data.slice(0));
        console.timeEnd('wade');

        // -----------------------------------------------------------

        var payload = [];

        for(var i = 0; i < data.length; i++){
            payload[i] = {id: i, content: data[i]};
        }

        // Note: fuse adds async?
        console.time('fuse');

        fuse = new Fuse(payload.slice(0), {
            keys: ['content'],
            id: 'id',
            shouldSort: true,
            threshold: 0.6,
            location: 0,
            distance: 100,
            findAllMatches: true,
            maxPatternLength: 32,
            minMatchCharLength: 1
        });

        console.timeEnd('fuse');

        // payload = null;
        // data = null;
        // return;

        // -----------------------------------------------------------

        jssearch = new JsSearch.Search('id');
        jssearch.addIndex('content');
        console.time('jssearch');
        jssearch.addDocuments(payload.slice(0));
        console.timeEnd('jssearch');

        // -----------------------------------------------------------

        var payload = [];

        for(var i = 0; i < data.length; i++){
            payload[i] = {id: i, text: data[i]};
        }

        jsii = new JSii();

        console.time('jsii');
        jsii.feedDocs(payload.slice(0));
        console.timeEnd('jsii');

        // -----------------------------------------------------------

        bm25 = new BM25();

        console.time('bm25');
        for(var i = 0; i < data.length; i++){
            bm25.addDocument({id: i, body: data[i]});
        }
        bm25.updateIdf();
        console.timeEnd('bm25');

        payload = null;

        // -----------------------------------------------------------

        console.time('fuzzysearch');
        fuzzysearch = new FuzzySearch({source:data.slice(0)});
        console.timeEnd('fuzzysearch');

        // -----------------------------------------------------------

        do_test('test-1', 'without breach of modesty', [493]);
        do_test('test-2', 'went softly stream', [446]);
        do_test('test-3', 'princes of the ambition', [72, 408]);
        do_test('test-4', 'five-thousand leagues', [2]);
        do_test('test-5', 'i already observed', [458, 346]);
        do_test('test-6', 'disgust the bigness', [175]);
        do_test('test-7', 'bignes of splaknuk', [146]);
        do_test('test-8', 'matematikal musikal instruments', [267]);
        do_test('test-9', 'composition of minerals gums juices vegetables', [303]);
        do_test('test-10', 'lalkon the camberlayhn', [99]);
        do_test('test-11', 'to be at all this is', [184]);
        do_test('test-12', 'matical sical strument', [267]);
        do_test('test-13', 'zero one three ten', [504]);

        // ---------------------------------------

        function do_test(id, query, ref){

            var nodes = document.getElementById(id).getElementsByTagName('td');

            nodes[0].innerHTML = query;

            for(var i = 1; i < nodes.length; i++){

                var results;

                switch(i){

                    case 1:
                        results = flexsearch.search(query, {suggest: true});
                        break;

                    case 2:
                        results = bulksearch.search(query, {suggest: true});
                        break;

                    case 3:
                        results = elasticsearch.search(query).map(function(val){return val.ref});
                        break;

                    case 4:
                        results = lunrsearch.search(query).map(function(val){return val.ref});
                        break;

                    case 5:
                        results = wadesearch(query).map(function(val){return val.index});
                        break;

                    case 6:
                        results = fuse.search(query);
                        break;

                    case 7:
                        results = jssearch.search(query).map(function(val){return val.id});
                        break;

                    case 8:
                        results = jsii.search(query).docs.map(function(val){return val.id});
                        break;

                    case 9:
                        results = bm25.search(query).map(function(val){return val.id});
                        break;

                    case 10:
                        results = fuzzysearch.search(query).map(function(val){
                            for(var i = 0; i < data.length; i++){
                                if(val === data[i]) return i;
                            }
                        });
                        break;
                }

                for(var a = 0; a < ref.length; a++){

                    var current = ref[a];

                    nodes[i].innerHTML = results[0] || '-';
                    nodes[i].style.color = '#fff';

                    if((results[0] === current) || (results[0] === String(current))){

                        nodes[i].style.backgroundColor = '#0a0';
                        break;
                    }
                    else if(!results.length || ((results.indexOf(current) === -1) && (results.indexOf(String(current)) === -1))){

                        if(nodes[i].style.backgroundColor !== 'orange'){

                            nodes[i].style.backgroundColor = '#f00';
                        }
                    }
                    else{

                        nodes[i].style.backgroundColor = 'orange';
                    }
                }
            }
        }

    }, 50);

</script>
</body>
</html>
